﻿//Copyright (c) 2010, William Dollins (www.zekiah.com)
//All rights reserved.

//Redistribution and use in source and binary forms, with or 
//without modification, are permitted provided that the following 
//conditions are met:

//Redistributions of source code must retain the above copyright notice, 
//this list of conditions and the following disclaimer.
//Redistributions in binary form must reproduce the above copyright notice, 
//this list of conditions and the following disclaimer in the documentation 
//and/or other materials provided with the distribution.
//Neither the name of Zekiah Technologies, Inc. nor the names of its contributors 
//may be used to endorse or promote products derived from this software without 
//specific prior written permission.

//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
//AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
//ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
//LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
//CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
//SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
//INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
//CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
//ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
//POSSIBILITY OF SUCH DAMAGE.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Net;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Windows.Forms;
using Zekiah.GeoCommons.ArcGIS.Framework;
using ESRI.ArcGIS.ArcMapUI;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Framework;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.DataSourcesFile;

namespace Zekiah.GeoCommons.ArcGIS.UI
{
    public partial class GCQueryForm : Form
    {

        private IMxDocument _mxd = null;
        private string _kmlHandler = "";

        public GCQueryForm(int OwnerHwnd, IMxDocument mxd)
        {
            InitializeComponent();
            this.comboBox1.SelectedIndex = 0;
            Win32Api.SetWindowLong(this.Handle.ToInt32(), -8, OwnerHwnd);
            _mxd = mxd;
            string appPath = this.GetType().Assembly.Location;
            string testPath = System.IO.Path.GetDirectoryName(appPath) + @"\test.kml";
            _kmlHandler = Win32Api.GetExecutable(testPath);
        }

        private void btnSearch_Click(object sender, EventArgs e)
        {
            this.Cursor = Cursors.WaitCursor;
            IEnvelope bounds = this._mxd.ActiveView.Extent;
            bounds.Project(this.getWGS84());
            ExecuteSearch(this.txtKeyword.Text, Convert.ToInt32(this.comboBox1.SelectedItem.ToString()), bounds.YMax, bounds.YMin, bounds.XMax, bounds.XMin);
        }

        private void ExecuteSearch(string term, int limit, double north, double south, double east, double west)
        {
            this.statusLabel.Text = "Executing search...";
            WebClient request = new WebClient();
            string url = String.Format("http://finder.geocommons.com/search.atom?query={0}&limit={1}&bbox={2},{3},{4},{5}", term, limit.ToString(), west, south, east, north);
            request.DownloadStringCompleted += new DownloadStringCompletedEventHandler(request_DownloadStringCompleted);
            request.DownloadStringAsync(new Uri(url)); 
        }

        void request_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            this.statusLabel.Text = "";
            if (e.Error == null)
            {
                this.lstResults.Items.Clear();
                string s = e.Result;
                XElement root = XElement.Parse(s);
                string n = root.Name.LocalName;
                XNamespace atom = "http://www.w3.org/2005/Atom";
                IEnumerable<XElement> entries = root.Elements(atom + "entry");
                foreach (XElement entry in entries)
                {
                    XElement title = entry.Elements(atom + "title").First();
                    IEnumerable<XElement> links =
                    (from el in entry.Elements(atom + "link")
                     where (string)el.Attribute("type") == "application/vnd.google-earth.kml+xml"
                     select el).Take(1);

                    XElement link = null;
                    if (links.Count() > 0)
                    {
                        link = links.First();
                    }
                    if (link != null)
                    {
                        string kml = links.Attributes("href").First().Value;
                        string shp = kml.Replace("kml", "zip");
                        string info = kml.Replace(".kml", "");
                        OverlayInfo overlay = new OverlayInfo { title = title.Value, shapelink = shp, kmllink = kml, infolink = info };
                        this.lstResults.Items.Add(overlay);
                    }
                }
            }
            else
            {
                MessageBox.Show(e.Error.ToString());
            }
            this.Cursor = Cursors.Default;
        }

        private void lstResults_SelectedIndexChanged(object sender, EventArgs e)
        {
            //OverlayInfo overlay = this.lstResults.SelectedItem as OverlayInfo;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            OverlayInfo overlay = this.lstResults.SelectedItem as OverlayInfo;
            if (_kmlHandler != "")
            {
                System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo(_kmlHandler);
                startInfo.Arguments = overlay.kmllink;
                System.Diagnostics.Process.Start(startInfo); ;
            }
            //System.Diagnostics.Process.Start(overlay.kmllink);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            this.Hide();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            this.statusLabel.Text = "Downloading selected data...";
            processDownload();
        }

        private void processDownload()
        {
            this.Cursor = Cursors.WaitCursor;
            try
            {       
                string tmp = System.IO.Path.GetTempPath(); //find user's temp folder
                OverlayInfo overlay = this.lstResults.SelectedItem as OverlayInfo;
                string file = System.IO.Path.GetFileName(overlay.shapelink);
                //build all of the necessary file info
                string filebase = file.ToLower().Replace(".zip", "");
                string outfolder = tmp + filebase; //output location
                System.IO.Directory.CreateDirectory(outfolder);
                string outpath = tmp + filebase + "\\" + file;
                //WebClient client = new WebClient();
                WebRequest req = WebRequest.Create(overlay.shapelink);
                WebResponse resp = req.GetResponse();
                Stream strm = resp.GetResponseStream();
                SaveStreamToFile(outpath, strm);
                //this.statusLabel.Text = "Extracting data...";
                string shapefile = Utils.ExtractFiles(outpath, outfolder, false);
                resp.Close();
                if (shapefile != "")
                {
                    this.statusLabel.Text = "Adding data to the map...";
                    string workspace = System.IO.Path.GetDirectoryName(shapefile);
                    string dataset = System.IO.Path.GetFileName(shapefile);
                    dataset = dataset.ToLower().Replace(".shp", "");
                    AddShapefile(workspace, dataset, overlay.title);
                }
                else
                {
                    MessageBox.Show("There was a problem opening your data. Either the download was corrupted or there was not a valid data file.", "Data Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "Error Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                this.statusLabel.Text = "";
                this.Cursor = Cursors.Default;
            }
        }

        private void SaveStreamToFile(string fileFullPath, Stream stream)
        {
            try
            {
                using(Stream fs = File.Open(fileFullPath, FileMode.Create) )
                {
                    byte []buf = new byte[1000];
                    int iRead = 0;
                    do
                    {
                        iRead = stream.Read(buf, 0, buf.Length);
                        if (iRead > 0)
                            fs.Write(buf, 0, iRead);
                    } while (iRead > 0);
                }                
            }
            catch (Exception ex)
            {
                //TODO: examine and throw
                throw ex;
                //MessageBox.Show(ex.ToString());
            }
        }

        /// <summary>
        /// Returns a reference to an ISpatialReference representing the WGS84 coordinate system.
        /// </summary>
        /// <returns>ISpatialReference (WGS84)</returns>
        private ESRI.ArcGIS.Geometry.ISpatialReference getWGS84()
        {
            ISpatialReferenceFactory oSpatialReferenceFactory = null;
            try
            {
                Type t = Type.GetTypeFromProgID("esriGeometry.SpatialReferenceEnvironment");
                System.Object obj = Activator.CreateInstance(t);
                oSpatialReferenceFactory = obj as ISpatialReferenceFactory;
                //oSpatialReferenceFactory = new SpatialReferenceEnvironmentClass();
                return oSpatialReferenceFactory.CreateGeographicCoordinateSystem((int)esriSRGeoCSType.esriSRGeoCS_WGS1984);
            }
            catch (Exception ex)
            {
                return GetSpatialReference(esriSRGeoCSType.esriSRGeoCS_WGS1984);
            }
        }

        private ISpatialReference GetSpatialReference(ESRI.ArcGIS.Geometry.esriSRGeoCSType FactoryCode)
        {
            ISpatialReferenceFactory oSpatialReferenceFactory = null;
            IGeographicCoordinateSystem oGCS = null;
            try
            {
                oSpatialReferenceFactory = new SpatialReferenceEnvironment();
                oGCS = oSpatialReferenceFactory.CreateGeographicCoordinateSystem((int)FactoryCode);
                return oGCS;
            }
            catch
            {
                return null;
            }
        }

        private void AddShapefile(string folder, string dataset, string description)
        {
            try
            {
                IWorkspaceFactory wkspcfact = new ShapefileWorkspaceFactoryClass() as IWorkspaceFactory;
                IFeatureWorkspace wkspc = wkspcfact.OpenFromFile(folder, 0) as IFeatureWorkspace;
                IFeatureLayer lyr = new FeatureLayerClass() as IFeatureLayer;
                IFeatureClass fc = wkspc.OpenFeatureClass(dataset);
                IGeoDataset gds = fc as IGeoDataset;
                IGeoDatasetSchemaEdit edit = gds as IGeoDatasetSchemaEdit;
                if (edit.CanAlterSpatialReference)
                {
                    //GeoCommons does not deliver a .prj file with
                    //shapefiles. This sets the spatial reference
                    edit.AlterSpatialReference(this.getWGS84());
                }
                lyr.FeatureClass = fc;
                lyr.Name = description;
                lyr.SpatialReference = this.getWGS84();
                _mxd.FocusMap.AddLayer(lyr); //_mxd is the current document open in ArcMap
            }
            catch (Exception ex)
            {
                throw ex;
                //TODO: put in better user notification
                //MessageBox.Show(ex.ToString());
            }
        }

        private void tmrValidate_Tick(object sender, EventArgs e)
        {
            this.btnKML.Enabled = (this.lstResults.SelectedItem != null) && (_kmlHandler != "");
            this.btnDownload.Enabled = (this.lstResults.SelectedItem != null);
        }

    }
}
